#include <GL/glut.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "ppm.c"

#define BUFSIZE 	512
#define ANGLE_MIN	0
#define ANGLE_1		45
#define ANGLE_2		90
#define ANGLE_3		135
#define ANGLE_4		180
#define ANGLE_5		225
#define ANGLE_6		270
#define ANGLE_7		315
#define ANGLE_MAX	360

typedef enum {
	IN_FRONT_RIGHT = 1,
	IN_TOP = 2,
	IN_FRONT_LEFT = 4,
	IN_BOTTOM = 8,
	IN_BACK_RIGHT = 16,
	IN_BACK_LEFT = 32,
	IN_MIDDLE_LR = 64,
	IN_MIDDLE_BT = 128,
	IN_MIDDLE_RL = 256
} Occurrence;

typedef enum {
	FRONT_RIGHT,
	TOP,
	FRONT_LEFT,
	BOTTOM,
	BACK_RIGHT,
	BACK_LEFT,
	MIDDLE_LR,
	MIDDLE_BT,
	MIDDLE_RL,
	NB_TRANCHES
} Tranche;

typedef enum {
	RED,
	BLUE,
	WHITE,
	GREEN,
	YELLOW,
	ORANGE,
	BLACK,
	NB_COLORS
} Color;

char * imageColor[NB_COLORS] = {"./img/rouge.ppm",
									"./img/bleu.ppm",
									"./img/blanc.ppm",
									"./img/vert.ppm",
									"./img/jaune.ppm",
									"./img/orange.ppm",
									"./img/noir.ppm"};

GLuint		texName[NB_COLORS];
GLubyte*	texture[NB_COLORS];
int width[NB_COLORS], height[NB_COLORS];

GLuint selectBuf[BUFSIZE];

int rubixSolved();
int getStableAngle(int degree);
void mouse(int button, int state, int x, int y);
void mousemotion(int x, int y);
void createFaces();
void processHits(GLint hits, GLuint buffer[]);
void stopPicking();
void startPicking(int cursorX, int cursorY);

typedef struct {
	float x;
	float y;
	float z;
} Point;

#define OCCURRENCES_MAX 3
#define NB_FACES_CUBE	6
typedef struct {
	int id;
	Point point;
	Color face[NB_FACES_CUBE];
	int occurs_in;
	/*int nb_occ;
	Tranche occurs_in[OCCURRENCES_MAX];*/
} Cube;

#define NB_CUBES_PER_FACE	9
typedef struct {
	Cube* cube[NB_CUBES_PER_FACE];
    int degree;
	Point pivot;
} rubixTranche;

#define NB_CUBES		27
struct {
	Cube cube[NB_CUBES];
	rubixTranche tranche[NB_TRANCHES];
	rubixTranche trancheInit[NB_TRANCHES];
} rubixCube;

int selected;

float xRotation = 0.0, yRotation = 0.0;
float xold, yold, anglex = 0, angley = 0, anglez = 0, presse = 0;
float anglex2 = 0;

float ex=3;
float ey=3;
float ez=3; //3;

float dimensionX = 1;
float dimensionY = 1;
float dimensionZ = 1;

int winX = 0;
int winY = 0;

int cube_selected = -1;

int direction = -1;
int side = 0;
int num_ligne = -1;
int angle_actu = 0;

void debugTranche(int t){
	switch(t){
		case FRONT_RIGHT:
			printf("FRONT_RIGHT\n");
			break;
		case TOP:
			printf("TOP\n");
			break;
		case FRONT_LEFT:
			printf("FRONT_LEFT\n");
			break;
		case BOTTOM:
			printf("BOTTOM\n");
			break;
		case BACK_RIGHT:
			printf("BACK_RIGHT\n");
			break;
		case BACK_LEFT:
			printf("BACK_LEFT\n");
			break;
		case MIDDLE_LR:
			printf("MIDDLE_LR\n");
			break;
		case MIDDLE_BT:
			printf("MIDDLE_BT\n");
			break;
		case MIDDLE_RL:
			printf("MIDDLE_RL\n");
			break;
	}
}

void debugColor(int c){
	switch(c){
		case RED:
			printf("RED\n");
			break;
		case BLUE:
			printf("BLUE\n");
			break;
		case WHITE:
			printf("WHITE\n");
			break;
		case GREEN:
			printf("GREEN\n");
			break;
		case YELLOW:
			printf("YELLOW\n");
			break;
		case ORANGE:
			printf("ORANGE\n");
			break;
		case BLACK:
			printf("BLACK\n");
			break;
	}
}

int cube_occurs_in(Cube cub, int tranches){
	int res = 0;
	if((cub.occurs_in & (tranches)) == tranches)
		res = 1;
	return res;
}

void init(void) {
	glClearColor (0.0, 1.0, 0.0, 0.0);
	glEnable (GL_DEPTH_TEST);

	// Textures
	int color;
	for(color = RED; color < NB_COLORS; color++)
		texture[color] = glmReadPPM(imageColor[color],
										&width[color], &height[color]);

	glGenTextures(NB_COLORS, texName);

	for(color = RED; color < NB_COLORS; color++){
		glBindTexture(GL_TEXTURE_2D, texName[color]);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width[color],
						height[color], 0, GL_RGB, GL_UNSIGNED_BYTE,
						texture[color]);
	}

	glEnable(GL_TEXTURE_2D);

	createFaces();

}

void createFaces() {
	int x, y, z;
	int compt = 0;
	int comptFace[NB_TRANCHES];
	for(compt = 0; compt < NB_TRANCHES; compt++)		comptFace[compt] = 0;

	compt = 0;
	for(x = 0; x < 3; x++) {
		for(y = 0; y < 3; y++) {
			for(z = 0; z < 3; z++) {
				rubixCube.cube[compt].point.x = x;
				rubixCube.cube[compt].point.y = y;
				rubixCube.cube[compt].point.z = z;
				rubixCube.cube[compt].id = compt;
				if(z == 0) {
					rubixCube.cube[compt].face[FRONT_RIGHT] = RED;
					/* Methode 1 */
					rubixCube.tranche[FRONT_RIGHT].cube[comptFace[FRONT_RIGHT]] = &rubixCube.cube[compt];
					comptFace[FRONT_RIGHT]++;
					/* Methode 2
					rubixCube.cube[compt].occurs_in[rubixCube.cube[compt].nb_occ] = FRONT_RIGHT;
					rubixCube.cube[compt].nb_occ++; */
					//printf("FRONT_RIGHT:%d\n", compt);
					rubixCube.cube[compt].occurs_in |= IN_FRONT_RIGHT;
				} else {
					rubixCube.cube[compt].face[FRONT_RIGHT] = BLACK;
				}
				if(y == 2) {
					rubixCube.cube[compt].face[TOP] = BLUE;
					/* Methode 1 */
					rubixCube.tranche[TOP].cube[comptFace[TOP]] = &rubixCube.cube[compt];
					comptFace[TOP]++;
					/* Methode 2
					rubixCube.cube[compt].occurs_in[rubixCube.cube[compt].nb_occ] = TOP;
					rubixCube.cube[compt].nb_occ++; */
					//printf("\t\tTOP:%d\n", compt);
					rubixCube.cube[compt].occurs_in |= IN_TOP;
				} else {
					rubixCube.cube[compt].face[TOP] = BLACK;
				}
				if(x == 2) {
					rubixCube.cube[compt].face[FRONT_LEFT] = WHITE;
					/* Methode 1 */
					rubixCube.tranche[FRONT_LEFT].cube[comptFace[FRONT_LEFT]] = &rubixCube.cube[compt];
					comptFace[FRONT_LEFT]++;
					/* Methode 2
					rubixCube.cube[compt].occurs_in[rubixCube.cube[compt].nb_occ] = FRONT_LEFT;
					rubixCube.cube[compt].nb_occ++; */
					//printf("\t\tFRONT_LEFT:%d\n", compt);
					rubixCube.cube[compt].occurs_in |= IN_FRONT_LEFT;
				} else {
					rubixCube.cube[compt].face[FRONT_LEFT] = BLACK;
				}
				if(y == 0) {
					rubixCube.cube[compt].face[BOTTOM] = GREEN;
					/* Methode 1 */
					rubixCube.tranche[BOTTOM].cube[comptFace[BOTTOM]] = &rubixCube.cube[compt];
					comptFace[BOTTOM]++;
					/* Methode 2
					rubixCube.cube[compt].occurs_in[rubixCube.cube[compt].nb_occ] = BOTTOM;
					rubixCube.cube[compt].nb_occ++; */
					//printf("\t\t\tBOTTOM:%d\n", compt);
					rubixCube.cube[compt].occurs_in |= IN_BOTTOM;
				} else {
					rubixCube.cube[compt].face[BOTTOM] = BLACK;
				}
				if(x == 0) {
					rubixCube.cube[compt].face[BACK_RIGHT] = YELLOW;
					/* Methode 1 */
					rubixCube.tranche[BACK_RIGHT].cube[comptFace[BACK_RIGHT]] = &rubixCube.cube[compt];
					comptFace[BACK_RIGHT]++;
					/* Methode 2
					rubixCube.cube[compt].occurs_in[rubixCube.cube[compt].nb_occ] = BACK_RIGHT;
					rubixCube.cube[compt].nb_occ++; */
					//printf("\t\t\t\tBACK_RIGHT:%d\n", compt);
					rubixCube.cube[compt].occurs_in |= IN_BACK_RIGHT;
				} else {
					rubixCube.cube[compt].face[BACK_RIGHT] = BLACK;
				}
				if(z == 2) {
					rubixCube.cube[compt].face[BACK_LEFT] = ORANGE;
					/* Methode 1 */
					rubixCube.tranche[BACK_LEFT].cube[comptFace[BACK_LEFT]] = &rubixCube.cube[compt];
					comptFace[BACK_LEFT]++;
					/* Methode 2
					rubixCube.cube[compt].occurs_in[rubixCube.cube[compt].nb_occ] = BACK_LEFT;
					rubixCube.cube[compt].nb_occ++; */
					//printf("\t\t\t\t\tBACK_LEFT:%d\n", compt);
					rubixCube.cube[compt].occurs_in |= IN_BACK_LEFT;
				} else {
					rubixCube.cube[compt].face[BACK_LEFT] = BLACK;
				}
				if (z == 1){
					/* Methode 1 */
					rubixCube.tranche[MIDDLE_LR].cube[comptFace[MIDDLE_LR]] = &rubixCube.cube[compt];
					comptFace[MIDDLE_LR]++;
					/* Methode 2
					rubixCube.cube[compt].occurs_in[rubixCube.cube[compt].nb_occ] = MIDDLE_LR;
					rubixCube.cube[compt].nb_occ++; */
					rubixCube.cube[compt].occurs_in |= IN_MIDDLE_LR;
				}
				if (y == 1) {
					/* Methode 1 */
					rubixCube.tranche[MIDDLE_BT].cube[comptFace[MIDDLE_BT]] = &rubixCube.cube[compt];
					comptFace[MIDDLE_BT]++;
					/* Methode 2
					rubixCube.cube[compt].occurs_in[rubixCube.cube[compt].nb_occ] = MIDDLE_BT;
					rubixCube.cube[compt].nb_occ++; */
					rubixCube.cube[compt].occurs_in |= IN_MIDDLE_BT;
				}
				if (x == 1) {
					/* Methode 1 */
					rubixCube.tranche[MIDDLE_RL].cube[comptFace[MIDDLE_RL]] = &rubixCube.cube[compt];
					comptFace[MIDDLE_RL]++;
					/* Methode 2
					rubixCube.cube[compt].occurs_in[rubixCube.cube[compt].nb_occ] = MIDDLE_RL;
					rubixCube.cube[compt].nb_occ++; */
					rubixCube.cube[compt].occurs_in |= IN_MIDDLE_RL;
				}
				compt++;
			}
		}
	}


	rubixCube.tranche[BOTTOM].pivot = (Point){0.0, 1.0, 0.0};
	rubixCube.tranche[BOTTOM].degree = 0;
	rubixCube.tranche[MIDDLE_BT].pivot = (Point){0.0, 1.0, 0.0};
	rubixCube.tranche[MIDDLE_BT].degree = 0;
	rubixCube.tranche[TOP].pivot = (Point){0.0, 1.0, 0.0};
	rubixCube.tranche[TOP].degree = 0;
	rubixCube.tranche[BACK_RIGHT].pivot = (Point){1.0, 0.0, 0.0};
	rubixCube.tranche[BACK_RIGHT].degree = 0;
	rubixCube.tranche[MIDDLE_RL].pivot = (Point){1.0, 0.0, 0.0};
	rubixCube.tranche[MIDDLE_RL].degree = 0;
	rubixCube.tranche[FRONT_LEFT].pivot = (Point){1.0, 0.0, 0.0};
	rubixCube.tranche[FRONT_LEFT].degree = 0;
	rubixCube.tranche[BACK_LEFT].pivot = (Point){0.0, 0.0, 1.0};
	rubixCube.tranche[BACK_LEFT].degree = 0;
	rubixCube.tranche[MIDDLE_LR].pivot = (Point){0.0, 0.0, 1.0};
	rubixCube.tranche[MIDDLE_LR].degree = 0;
	rubixCube.tranche[FRONT_RIGHT].pivot = (Point){0.0, 0.0, 1.0};
	rubixCube.tranche[FRONT_RIGHT].degree = 0;
	
	int iz = 0;
	for(iz=0;iz<NB_TRANCHES;iz++) {
	    rubixCube.trancheInit[iz] = rubixCube.tranche[iz];
	    rubixCube.tranche[iz].degree = 0;
	    rubixCube.trancheInit[iz].degree = 0;
	}
}

void createCube(Cube cub) {
	glBindTexture(GL_TEXTURE_2D, texName[cub.face[FRONT_RIGHT]]);
	glBegin(GL_POLYGON);
			glTexCoord2f(0.0, 0.0); glVertex3f (cub.point.x, cub.point.y, cub.point.z);
			glTexCoord2f(1.0, 0.0); glVertex3f (cub.point.x, cub.point.y+1, cub.point.z);
			glTexCoord2f(1.0, 1.0); glVertex3f (cub.point.x+1, cub.point.y+1, cub.point.z);
			glTexCoord2f(0.0, 1.0); glVertex3f (cub.point.x+1, cub.point.y, cub.point.z);
	glEnd();

	glBindTexture(GL_TEXTURE_2D, texName[cub.face[TOP]]);
	glBegin(GL_POLYGON);
			glTexCoord2f(0.0, 0.0); glVertex3f (cub.point.x, cub.point.y+1, cub.point.z);
			glTexCoord2f(1.0, 0.0); glVertex3f (cub.point.x, cub.point.y+1, cub.point.z+1);
			glTexCoord2f(1.0, 1.0); glVertex3f (cub.point.x+1, cub.point.y+1, cub.point.z+1);
			glTexCoord2f(0.0, 1.0); glVertex3f (cub.point.x+1, cub.point.y+1, cub.point.z);
	glEnd();

	glBindTexture(GL_TEXTURE_2D, texName[cub.face[FRONT_LEFT]]);
	glBegin(GL_POLYGON);
			glTexCoord2f(0.0, 0.0); glVertex3f (cub.point.x+1, cub.point.y, cub.point.z);
			glTexCoord2f(1.0, 0.0); glVertex3f (cub.point.x+1, cub.point.y, cub.point.z+1);
			glTexCoord2f(1.0, 1.0); glVertex3f (cub.point.x+1, cub.point.y+1, cub.point.z+1);
			glTexCoord2f(0.0, 1.0); glVertex3f (cub.point.x+1, cub.point.y+1, cub.point.z);
	glEnd();

	glBindTexture(GL_TEXTURE_2D, texName[cub.face[BOTTOM]]);
	glBegin(GL_POLYGON);
			glTexCoord2f(0.0, 0.0); glVertex3f (cub.point.x, cub.point.y, cub.point.z);
			glTexCoord2f(1.0, 0.0); glVertex3f (cub.point.x, cub.point.y, cub.point.z+1);
			glTexCoord2f(1.0, 1.0); glVertex3f (cub.point.x+1, cub.point.y, cub.point.z+1);
			glTexCoord2f(0.0, 1.0); glVertex3f (cub.point.x+1, cub.point.y, cub.point.z);
	glEnd();

	glBindTexture(GL_TEXTURE_2D, texName[cub.face[BACK_RIGHT]]);
	glBegin(GL_POLYGON);
			glTexCoord2f(0.0, 0.0); glVertex3f (cub.point.x, cub.point.y, cub.point.z);
			glTexCoord2f(1.0, 0.0); glVertex3f (cub.point.x, cub.point.y, cub.point.z+1);
			glTexCoord2f(1.0, 1.0); glVertex3f (cub.point.x, cub.point.y+1, cub.point.z+1);
			glTexCoord2f(0.0, 1.0); glVertex3f (cub.point.x, cub.point.y+1, cub.point.z);
	glEnd();

	glBindTexture(GL_TEXTURE_2D, texName[cub.face[BACK_LEFT]]);
	glBegin(GL_POLYGON);
			glTexCoord2f(0.0, 0.0); glVertex3f (cub.point.x, cub.point.y, cub.point.z+1);
			glTexCoord2f(1.0, 0.0); glVertex3f (cub.point.x, cub.point.y+1, cub.point.z+1);
			glTexCoord2f(1.0, 1.0); glVertex3f (cub.point.x+1, cub.point.y+1, cub.point.z+1);
			glTexCoord2f(0.0, 1.0); glVertex3f (cub.point.x+1, cub.point.y, cub.point.z+1);
	glEnd();
}

void drawRubix() {
	int i, j;
	for(i = 0; i < NB_CUBES; i++) {
		glPushMatrix();

		if((rubixCube.cube[i].occurs_in & ((int)pow(2, num_ligne))) == ((int)pow(2, num_ligne))){
			glTranslatef(1.5,1.5,1.5);
			glRotatef(rubixCube.tranche[num_ligne].degree, 
								rubixCube.tranche[num_ligne].pivot.x,
								rubixCube.tranche[num_ligne].pivot.y,
								rubixCube.tranche[num_ligne].pivot.z);
			glTranslatef(-1.5,-1.5,-1.5);
		}
		//
		glPushName(i);
		createCube(rubixCube.cube[i]);
		glPopName();
		glPopMatrix();
	}
}


void display(void) {
	glLoadIdentity ();
	gluLookAt (ex, ey, ez,							0.0, 0.0, 0.0,									0.0, 1.0, 0.0);
	//printf("Position de l'oeil xyz = %f %f %f \n", ex, ey, ez);
	glClear (GL_COLOR_BUFFER_BIT);
	glClear (GL_DEPTH_BUFFER_BIT);

	glRotatef(-anglex, 0.0, 1.0, 0.0);
	glRotatef(-angley, 1.0, 0.0, 0.0);
	glRotatef(-anglez, 0.0, 0.0, 1.0);

	glTranslatef(-1.5,-1.5,-1.5);

	drawRubix();

	glutSwapBuffers();
}

void reshape (int w, int h) {
	winX = w;
	winY = h;
	glViewport (0, 0, (GLsizei) w, (GLsizei) h);
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity ();
	gluPerspective(90, w/h, 1, 20);
	glMatrixMode (GL_MODELVIEW);
}



void keyboard(unsigned char key, int x, int y) {
	switch (key) {
		case 27:
			exit(0);
			break;
	}
	glutPostRedisplay();
}

int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode (GLUT_DOUBLE|GLUT_RGB|GLUT_DEPTH);
	glutInitWindowSize (500, 500);
	glutInitWindowPosition (100, 100);
	glutCreateWindow (argv[0]);
	init ();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);

	glutMouseFunc(mouse);
	glutMotionFunc(mousemotion);

	glutMainLoop();
	return 0;
}

void changeLine(int degree) {
  //rubixCube.lignesInit[];
  int iz = 0;
    for(iz=0;iz<NB_TRANCHES;iz++) {
        rubixCube.tranche[iz] = rubixCube.trancheInit[iz];
        rubixCube.tranche[iz].degree = 0;
    }
  /*if(degree==0) {
    rubixCube.faces = rubixCube.facesInit;
  }  */
  if(degree==ANGLE_2/* && degree<91*/) {
    //rubixCube.faces = rubixCube.facesInit;
    rubixCube.tranche[3] = rubixCube.trancheInit[6];
    rubixCube.tranche[4] = rubixCube.trancheInit[7];
    rubixCube.tranche[5] = rubixCube.trancheInit[8];
    // ------
    rubixCube.tranche[6] = rubixCube.trancheInit[5];
    rubixCube.tranche[7] = rubixCube.trancheInit[4];
    rubixCube.tranche[8] = rubixCube.trancheInit[3];
  }
  if(degree==ANGLE_4) {
    //rubixCube.faces = rubixCube.facesInit;
    rubixCube.tranche[3] = rubixCube.trancheInit[5];
    rubixCube.tranche[4] = rubixCube.trancheInit[4];
    rubixCube.tranche[5] = rubixCube.trancheInit[3];
    // ------
    rubixCube.tranche[6] = rubixCube.trancheInit[8];
    rubixCube.tranche[7] = rubixCube.trancheInit[7];
    rubixCube.tranche[8] = rubixCube.trancheInit[6];
  }
  if(degree==ANGLE_6) {
    //rubixCube.faces = rubixCube.facesInit;
    rubixCube.tranche[3] = rubixCube.trancheInit[8];
    rubixCube.tranche[4] = rubixCube.trancheInit[7];
    rubixCube.tranche[5] = rubixCube.trancheInit[6];
    // ------
    rubixCube.tranche[6] = rubixCube.trancheInit[3];
    rubixCube.tranche[7] = rubixCube.trancheInit[4];
    rubixCube.tranche[8] = rubixCube.trancheInit[5];
  }
}

void mouse(int button, int state, int x, int y){
	int iz = 0;
	int nb_switch = 0;
	int *switch_table = (int*)malloc(4*sizeof(int));
	Cube* top[3];
	int top_cpt = 0;
	Cube* br[3];
	int br_cpt = 0;
	Cube* bot[3];
	int bot_cpt = 0;
	Cube* fl[3];
	int fl_cpt = 0;
	int i, j, k;
	rubixTranche tmp_tranch;
	if(button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) {
		startPicking(x,y);
		//draw object
		drawRubix();
		stopPicking();
		presse = 2;
		xold = x;
		yold = y;
		if(x<=250) {
		    side = 0; //gauche
		} else {
		    side = 1; //droite
		}
	}
	if(button == GLUT_RIGHT_BUTTON && state == GLUT_UP){
		presse = 0;
		printf("num_ligne=%d\n", num_ligne);
		tmp_tranch = rubixCube.tranche[num_ligne];
		nb_switch = (int)(getStableAngle(rubixCube.tranche[num_ligne].degree) / ANGLE_2);
		printf("nb_switch:%d\n", nb_switch);
		printf("degree:%d\n", rubixCube.tranche[num_ligne].degree);
		switch(num_ligne){
			case BOTTOM:
			case MIDDLE_BT:
			case TOP:
				switch_table=(int[4]){FRONT_LEFT, FRONT_RIGHT, BACK_RIGHT, BACK_LEFT};
				for(i = 0; i < NB_CUBES_PER_FACE; i++){
					if(cube_occurs_in((Cube)(*tmp_tranch.cube[i]), IN_BACK_LEFT)){
						top[top_cpt] = tmp_tranch.cube[i];
						top_cpt++;
					}
					if(cube_occurs_in((Cube)(*tmp_tranch.cube[i]), IN_BACK_RIGHT)){
						br[br_cpt] = tmp_tranch.cube[i];
						br_cpt++;
					}
					if(cube_occurs_in((Cube)(*tmp_tranch.cube[i]), IN_FRONT_RIGHT)){
						bot[bot_cpt] = tmp_tranch.cube[i];
						bot_cpt++;
					}
					if(cube_occurs_in((Cube)(*tmp_tranch.cube[i]), IN_FRONT_LEFT)){
						fl[fl_cpt] = tmp_tranch.cube[i];
						fl_cpt++;
					}
					
				}
				// changement de couleur
				
				for(iz=0;iz<3;iz++) {
					int iw = 0;
					for(iw=0;iw<nb_switch;iw++) {
						if(rubixCube.tranche[num_ligne].degree > 0) {
							int c = top[iz]->face[BACK_LEFT];
							top[iz]->face[BACK_LEFT] = br[iz]->face[BACK_RIGHT];
							br[iz]->face[BACK_RIGHT] = bot[iz]->face[FRONT_RIGHT];
							bot[iz]->face[FRONT_RIGHT] = fl[iz]->face[FRONT_LEFT];
							fl[iz]->face[FRONT_LEFT] = c;
						} else {
							int c = top[iz]->face[BACK_LEFT];
							top[iz]->face[BACK_LEFT] = fl[iz]->face[FRONT_LEFT];
							fl[iz]->face[FRONT_LEFT] = bot[iz]->face[FRONT_RIGHT];
							bot[iz]->face[FRONT_RIGHT] = br[iz]->face[BACK_RIGHT];
							br[iz]->face[BACK_RIGHT] = c;
						}
					}
				}
				break;
			case BACK_LEFT:
			case MIDDLE_LR:
			case FRONT_RIGHT:
				switch_table=(int[4]){TOP, BACK_RIGHT, BOTTOM, FRONT_LEFT};
				for(i = 0; i < NB_CUBES_PER_FACE; i++){
					if(cube_occurs_in((Cube)(*tmp_tranch.cube[i]), IN_TOP)){
						top[top_cpt] = tmp_tranch.cube[i];
						top_cpt++;
					}
					if(cube_occurs_in((Cube)(*tmp_tranch.cube[i]), IN_BACK_RIGHT)){
						br[br_cpt] = tmp_tranch.cube[i];
						br_cpt++;
					}
					if(cube_occurs_in((Cube)(*tmp_tranch.cube[i]), IN_BOTTOM)){
						bot[bot_cpt] = tmp_tranch.cube[i];
						bot_cpt++;
					}
					if(cube_occurs_in((Cube)(*tmp_tranch.cube[i]), IN_FRONT_LEFT)){
						fl[fl_cpt] = tmp_tranch.cube[i];
						fl_cpt++;
					}
					
				}
				// changement de couleur
				for(iz=0;iz<3;iz++) {
					int iw = 0;
					for(iw=0;iw<nb_switch;iw++) {
						if(rubixCube.tranche[num_ligne].degree < 0) {
							int c = top[iz]->face[TOP];
							top[iz]->face[TOP] = br[iz]->face[BACK_RIGHT];
							br[iz]->face[BACK_RIGHT] = bot[iz]->face[BOTTOM];
							bot[iz]->face[BOTTOM] = fl[iz]->face[FRONT_LEFT];
							fl[iz]->face[FRONT_LEFT] = c;
						} else {
							int c = top[iz]->face[TOP];
							top[iz]->face[TOP] = fl[iz]->face[FRONT_LEFT];
							fl[iz]->face[FRONT_LEFT] = bot[iz]->face[BOTTOM];
							bot[iz]->face[BOTTOM] = br[iz]->face[BACK_RIGHT];
							br[iz]->face[BACK_RIGHT] = c;
						}
					}
				}
				break;
			case BACK_RIGHT:
			case MIDDLE_RL:
			case FRONT_LEFT:
				switch_table=(int[4]){TOP, BACK_LEFT, BOTTOM, FRONT_RIGHT};
				for(i = 0; i < NB_CUBES_PER_FACE; i++){
					if(cube_occurs_in((Cube)(*tmp_tranch.cube[i]), IN_TOP)){
						top[top_cpt] = tmp_tranch.cube[i];
						top_cpt++;
					}
					if(cube_occurs_in((Cube)(*tmp_tranch.cube[i]), IN_BACK_LEFT)){
						br[br_cpt] = tmp_tranch.cube[i];
						br_cpt++;
					}
					if(cube_occurs_in((Cube)(*tmp_tranch.cube[i]), IN_BOTTOM)){
						bot[bot_cpt] = tmp_tranch.cube[i];
						bot_cpt++;
					}
					if(cube_occurs_in((Cube)(*tmp_tranch.cube[i]), IN_FRONT_RIGHT)){
						fl[fl_cpt] = tmp_tranch.cube[i];
						fl_cpt++;
					}
					
				}
				// changement de couleur
				for(iz=0;iz<3;iz++) {
					int iw = 0;
					for(iw=0;iw<nb_switch;iw++) {
						if(rubixCube.tranche[num_ligne].degree < 0) {
							int c = top[iz]->face[TOP];
							top[iz]->face[TOP] = br[iz]->face[BACK_LEFT];
							br[iz]->face[BACK_LEFT] = bot[iz]->face[BOTTOM];
							bot[iz]->face[BOTTOM] = fl[iz]->face[FRONT_RIGHT];
							fl[iz]->face[FRONT_RIGHT] = c;
						} else {
							int c = top[iz]->face[TOP];
							top[iz]->face[TOP] = fl[iz]->face[FRONT_RIGHT];
							fl[iz]->face[FRONT_RIGHT] = bot[iz]->face[BOTTOM];
							bot[iz]->face[BOTTOM] = br[iz]->face[BACK_LEFT];
							br[iz]->face[BACK_LEFT] = c;
						}
					}
				}
				break;
		}
		direction = -1;
		if(num_ligne != -1) {
		  rubixCube.tranche[num_ligne].degree = ANGLE_MIN;
		  num_ligne = -1;
		}
		if(rubixSolved()) printf("Rubiks Solved !\n");
		glutPostRedisplay();
	}
	if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
		presse = 1;
		xold = x;
		yold = y;
	}
	if(button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
	  if(anglex > ANGLE_MIN && anglex <= ANGLE_1)
			anglex = ANGLE_MIN;
		else if(anglex > ANGLE_1 && anglex <= ANGLE_3)
			anglex = ANGLE_2;
		else if(anglex > ANGLE_3 && anglex <= ANGLE_5)
			anglex = ANGLE_4;
		else if(anglex > ANGLE_5 && anglex <= ANGLE_7)
			anglex = ANGLE_6;
		else if(anglex > ANGLE_7 && anglex <= ANGLE_MAX)
          anglex = ANGLE_MIN;
        else if(anglex < ANGLE_MIN && anglex >= -ANGLE_1)
          anglex = -ANGLE_MIN;
        else if(anglex < -ANGLE_1 && anglex >= -ANGLE_3)
          anglex = -ANGLE_2;
        else if(anglex < -ANGLE_3 && anglex >= -ANGLE_5)
          anglex = -ANGLE_4;
        else if(anglex < -ANGLE_5 && anglex >= -ANGLE_7)
          anglex = -ANGLE_6;
        else if(anglex < -ANGLE_7 && anglex >= -ANGLE_MAX)
          anglex = ANGLE_MIN;

      angle_actu = anglex;
        changeLine(anglex);
	    presse = 0;
	    glutPostRedisplay();
	}
}



void mousemotion(int x, int y) {
	int i;
	if(presse == 2) {
		//anglex2 = ((int)(anglex2 + (x-xold))) % ANGLE_MAX;
		//if(anglex2 < ANGLE_MIN) anglex2 = ANGLE_MAX - anglex2;
		if(direction == -1) {
		    if(x != xold) {
		        direction = 0;
		    } else if(y != yold) {
		        direction = 1;
		    }
		}
		if(cube_selected != -1) {
        int num = 0;
		    //rubixCube.cube[cube_selected].point.x = rubixCube.cube[cube_selected].point.x + 1;
			if(direction==0) { // x && (gauche || droite)c
				if(cube_occurs_in(rubixCube.cube[cube_selected], IN_BOTTOM))
					num_ligne = BOTTOM;
				if(cube_occurs_in(rubixCube.cube[cube_selected], IN_MIDDLE_BT))
					num_ligne = MIDDLE_BT;
				if(cube_occurs_in(rubixCube.cube[cube_selected], IN_TOP))
					num_ligne = TOP;
            	num = (x - xold);
		    }
		    if((direction==1 && side==1 && (anglex==ANGLE_MIN || anglex==ANGLE_4 || anglex==-ANGLE_4))
			|| (direction==1 && side==0 && (anglex==ANGLE_2 || anglex==ANGLE_6 || anglex==-ANGLE_2 || anglex==-ANGLE_6))) { // y && droite
		        if(cube_occurs_in(rubixCube.cube[cube_selected], IN_BACK_LEFT))
		            num_ligne = BACK_LEFT;
		        if(cube_occurs_in(rubixCube.cube[cube_selected], IN_MIDDLE_LR))
		            num_ligne = MIDDLE_LR;
		        if(cube_occurs_in(rubixCube.cube[cube_selected], IN_FRONT_RIGHT))
		            num_ligne = FRONT_RIGHT;

	            if(anglex==ANGLE_2 || anglex==-ANGLE_6)
	              rubixCube.tranche[num_ligne].pivot = (Point){0.0, 0.0 , 1.0};
	            if(anglex==ANGLE_4 || anglex==-ANGLE_4)
	              rubixCube.tranche[num_ligne].pivot = (Point){0.0, 0.0 , -1.0};
	            if(anglex==ANGLE_6 || anglex==-ANGLE_2)
	              rubixCube.tranche[num_ligne].pivot = (Point){0.0, 0.0 , -1.0};
            	num = (yold - y);
		    }
		    if((direction==1 && side==0 && (anglex==ANGLE_MIN || anglex==ANGLE_4 || anglex==-ANGLE_4))
			|| (direction==1 && side==1 && (anglex==ANGLE_2 || anglex==ANGLE_6 || anglex==-ANGLE_2 || anglex==-ANGLE_6))) { // y && gauche
		       if(cube_occurs_in(rubixCube.cube[cube_selected], IN_BACK_RIGHT))
		            num_ligne = BACK_RIGHT;
		        if(cube_occurs_in(rubixCube.cube[cube_selected], IN_MIDDLE_RL))
		            num_ligne = MIDDLE_RL;
		        if(cube_occurs_in(rubixCube.cube[cube_selected], IN_FRONT_LEFT))
		           num_ligne = FRONT_LEFT;

		        if(anglex==ANGLE_2 || anglex==-ANGLE_6)
		          rubixCube.tranche[num_ligne].pivot = (Point){-1.0, 0.0 , 0.0};
	            if(anglex==ANGLE_4 || anglex==-ANGLE_4)
	              rubixCube.tranche[num_ligne].pivot = (Point){-1.0, 0.0 , 0.0};
	            if(anglex==ANGLE_6 || anglex==-ANGLE_2)
	              rubixCube.tranche[num_ligne].pivot = (Point){1.0, 0.0 , 0.0};

            	num = (y - yold);
		    }

		    if(num_ligne != -1)
		      rubixCube.tranche[num_ligne].degree = ((int)(rubixCube.tranche[num_ligne].degree + num)) % ANGLE_MAX;
		    
		}
		//printf("{ anglex2=%.2f | x=%d | xold=%.2f | direction=%d }\n", anglex2, x, xold, direction);
		glutPostRedisplay();
	}
	if(presse == 1) {
		anglex = anglex - (x-xold);
		//angley = angley + (y-yold);
		if(anglex>=ANGLE_MAX || anglex<=-ANGLE_MAX) {
			anglex = ANGLE_MIN;
		}
		if(angley>=ANGLE_MAX || angley<=-ANGLE_MAX) {
			angley = ANGLE_MIN;
		}
		if(anglez>=ANGLE_MAX || anglez<=-ANGLE_MAX) {
			anglez = ANGLE_MIN;
		}

		glutPostRedisplay();
	}
	xold = x;
	yold = y;
}


//picking
void startPicking(int cursorX, int cursorY) {
	GLint viewport[4];

	glSelectBuffer(BUFSIZE,selectBuf);
	glRenderMode(GL_SELECT);

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();

	glGetIntegerv(GL_VIEWPORT,viewport);
	gluPickMatrix(cursorX,viewport[3]-cursorY,1,1,viewport);
	gluPerspective(90, winX/winY, 1, 20);
	glMatrixMode(GL_MODELVIEW);
	glInitNames();
}

void stopPicking() {
	int hits;

	// restoring the original projection matrix
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glFlush();

	// returning to normal rendering mode
	hits = glRenderMode(GL_RENDER);

	// if there are hits process them
	if (hits != 0)
		processHits(hits,selectBuf);
}

void processHits(GLint hits, GLuint buffer[]) {
	unsigned int i, j;
	GLuint names, *ptr, minZ,*ptrNames, numberOfNames;

	printf ("hits = %d\n", hits);
	ptr = (GLuint *) buffer;
	minZ = 0xffffffff;
	for (i = 0; i < hits; i++) {
			names = *ptr;
		ptr++;
		if (*ptr < minZ) {
			numberOfNames = names;
			minZ = *ptr;
			ptrNames = ptr+2;
		}

		ptr += names+2;
	}
	printf ("The closest hit names are ");
	ptr = ptrNames;
	selected = *ptr;
	for (j = 0; j < numberOfNames; j++,ptr++) {
		printf ("%d ", *ptr);
		cube_selected = *ptr;
		
	}
	printf ("\n");

}

int getStableAngle(int degree){
	int angle = ANGLE_MIN;
	
	if(abs(degree) > ANGLE_MIN && abs(degree) <= ANGLE_1)
		angle = ANGLE_MIN;
	else if(abs(degree) > ANGLE_1 && abs(degree) <= ANGLE_3)
		angle = ANGLE_2;
	else if(abs(degree) > ANGLE_3 && abs(degree) <= ANGLE_5)
		angle = ANGLE_4;
	else if(abs(degree) > ANGLE_5 && abs(degree) <= ANGLE_7)
		angle = ANGLE_6;
	else if(abs(degree) > ANGLE_7 && abs(degree) <= ANGLE_MAX)
		angle = ANGLE_MIN;
	
	return angle;
}

int rubixSolved(){
	int solved=1;
	int color=-1;
	int face = 0;
	int cube = 0;
	while(solved==1 && face < NB_FACES_CUBE){
		while(solved==1 && cube < NB_CUBES_PER_FACE){
			if(color==-1) color = rubixCube.tranche[face].cube[cube]->face[face];
			if(rubixCube.tranche[face].cube[cube]->face[face] != color) solved = 0;
			cube++;
		}
		cube = 0;
		color=-1;
		face++;
	}

	return solved;
}

